﻿using System;
using System.Collections.Generic;
using System.Linq;
using WinRTFramework.Common;
using WinRTFramework.Contracts.Settings;
using WinRTFramework.FrameworkConfiguration;
using WinRTFramework.Interfaces.Contracts.Settings.View;
using WinRTFramework.Interfaces.Contracts.Settings.ViewModel;
using Windows.Foundation;
using Windows.UI.ApplicationSettings;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media.Animation;

namespace WinRTFramework.FrameworkCore.Managers
{
    internal class SettingsManager
    {
        private IIoCAdapter _iocAdapter;
        private readonly IViewViewModelMapper _viewMapper;
        private List<ISupportPermanentSettingsFlyout> _permanentSettingsFlyouts;
        private List<Popup> _settingsPopupToDismissOnWindowsDeactivate = new List<Popup>();

        private const int SmallSettingsFlyoutWidth = 346;
        private const int LargeSettingsFlyoutWidth = 646;

        private SettingsManager(IIoCAdapter iocAdapter, IViewViewModelMapper viewMapper)
        {
            _iocAdapter = iocAdapter;
            _viewMapper = viewMapper;
        }

        private void Initialize()
        {
            SettingsPane.GetForCurrentView().CommandsRequested += SettingsService_CommandsRequested;
            Window.Current.Activated += Window_Activated;

            EvaluatePermanentSettings();
        }

        private void EvaluatePermanentSettings()
        {
            _permanentSettingsFlyouts = new List<ISupportPermanentSettingsFlyout>();

            foreach(var permanentSetting in _iocAdapter.GetAllRegisteredTypesThatImplement<ISupportPermanentSettingsFlyout>())
            {
                _permanentSettingsFlyouts.Add((ISupportPermanentSettingsFlyout) _iocAdapter.GetInstanceOfType(permanentSetting));
            }
        }

        void Window_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
        {
            if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
            {
                foreach(Popup popupToClose in _settingsPopupToDismissOnWindowsDeactivate.ToArray())
                    popupToClose.IsOpen = false;
            }
        }

        void SettingsService_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
        {
            foreach (ISupportSettingsFlyout settingsFlyout in GetAllSettingsThatAreValidInCurrentContextOrdered())
            {
                ISupportSettingsFlyout localCopy = settingsFlyout;
                args.Request.ApplicationCommands.Add(new SettingsCommand(Guid.NewGuid().ToString(), settingsFlyout.Label, command => OnSettingOpening(command, localCopy)));
            }
        }

        private void OnSettingOpening(IUICommand command, ISupportSettingsFlyout settingsFlyout)
        {
            Popup popup = CreatePopupForSettingsFlyout(settingsFlyout);
            settingsFlyout.ReturnToSettingsCommand = new DelegateCommand(() => OnReturnToSettingsFromPopup(popup));
            popup.DataContext = settingsFlyout;
            popup.IsOpen = true;
        }

        private void OnReturnToSettingsFromPopup(Popup popup)
        {
            popup.IsOpen = false;

            // If the app is not snapped, then the back button shows the Settings pane again.
            if (Windows.UI.ViewManagement.ApplicationView.Value != Windows.UI.ViewManagement.ApplicationViewState.Snapped)
            {
                SettingsPane.Show();
            }
        }

        private Popup CreatePopupForSettingsFlyout(ISupportSettingsFlyout settingsFlyout)
        {
            Rect windowBounds = Window.Current.Bounds;

            int settingsWidth = 0;
            switch(settingsFlyout.Size)
            {
                case SettingsFlyoutSize.Small:
                    settingsWidth = SmallSettingsFlyoutWidth;
                    break;
                case SettingsFlyoutSize.Large:
                    settingsWidth = LargeSettingsFlyoutWidth;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            Type settingsContentType = _viewMapper.GetViewTypeForViewModelType(settingsFlyout.GetType(), null);
            Control settingsContent = (Control)_iocAdapter.GetInstanceOfType(settingsContentType);
            settingsContent.Width = settingsWidth;
            settingsContent.Height = windowBounds.Height;

            if (settingsContent is ICareAboutSettingsEdgeLocation)
                ((ICareAboutSettingsEdgeLocation) settingsContent).SettingsEdgeLocation = SettingsPane.Edge;

            // Create a Popup window which will contain our flyout.
            var settingsPopup = new Popup();
            settingsPopup.IsLightDismissEnabled = true;
            settingsPopup.Width = settingsWidth;
            settingsPopup.Height = windowBounds.Height;

            // Add the proper animation for the panel.
            settingsPopup.ChildTransitions = new TransitionCollection();
            settingsPopup.ChildTransitions.Add(new PaneThemeTransition()
            {
                Edge = (SettingsPane.Edge == SettingsEdgeLocation.Right) ?
                       EdgeTransitionLocation.Right :
                       EdgeTransitionLocation.Left
            });

            // Place the SettingsFlyout inside our Popup window.
            settingsPopup.Child = settingsContent;

            // Let's define the location of our Popup.
            settingsPopup.SetValue(Canvas.LeftProperty, SettingsPane.Edge == SettingsEdgeLocation.Right ? (windowBounds.Width - settingsWidth) : 0);
            settingsPopup.SetValue(Canvas.TopProperty, 0);

            settingsPopup.Closed += (sender, o) => _settingsPopupToDismissOnWindowsDeactivate.Remove(settingsPopup);
            _settingsPopupToDismissOnWindowsDeactivate.Add(settingsPopup);

            return settingsPopup;
        }

        private IEnumerable<ISupportSettingsFlyout> GetAllSettingsThatAreValidInCurrentContextOrdered()
        {
            List<ISupportSettingsFlyout> allSettings = new List<ISupportSettingsFlyout>(_permanentSettingsFlyouts);

            var currentPage = ((Frame)Window.Current.Content).Content as Page;
            if (currentPage != null)
            {
                var contextSpecificSettingsProvider = currentPage.DataContext as ISupportContextSpecificSettings;
                if(contextSpecificSettingsProvider != null)
                {
                    allSettings.AddRange(contextSpecificSettingsProvider.GetSettingsFlyoutViewModels());
                }
            }

            return allSettings.OrderBy(k => k.DisplayOrder);
        }

        public static SettingsManager CreateFrom(IIoCAdapter iocAdapter, IViewViewModelMapper viewMapper)
        {
            var service = new SettingsManager(iocAdapter, viewMapper);
            service.Initialize();
            return service;
        }
    }
}
